package com.itheima.prize.api.action;

import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.itheima.prize.api.config.LuaScript;
import com.itheima.prize.commons.config.RabbitKeys;
import com.itheima.prize.commons.config.RedisKeys;
import com.itheima.prize.commons.db.entity.*;
import com.itheima.prize.commons.db.mapper.CardGameMapper;
import com.itheima.prize.commons.db.service.CardGameService;
import com.itheima.prize.commons.utils.ApiResult;
import com.itheima.prize.commons.utils.RedisUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.text.SimpleDateFormat;
import java.util.*;

@RestController
@RequestMapping("/api/act")
@Api(tags = {"抽奖模块"})
public class ActController {

    @Autowired
    private RedisUtil redisUtil;
    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Autowired
    private LuaScript luaScript;

    @GetMapping("/limits/{gameid}")
    @ApiOperation(value = "剩余次数")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "gameid", value = "活动id", example = "1", required = true)
    })
    public ApiResult<Object> limits(@PathVariable int gameid, HttpServletRequest request) {
        //获取活动基本信息
        CardGame game = (CardGame) redisUtil.get(RedisKeys.INFO + gameid);
        if (game == null) {
            return new ApiResult<>(-1, "活动未加载", null);
        }
        //获取当前用户
        HttpSession session = request.getSession();
        CardUser user = (CardUser) session.getAttribute("user");
        if (user == null) {
            return new ApiResult(-1, "未登陆", null);
        }
        //用户可抽奖次数
        Integer enter = (Integer) redisUtil.get(RedisKeys.USERENTER + gameid + "_" + user.getId());
        if (enter == null) {
            enter = 0;
        }
        //根据会员等级，获取本活动允许的最大抽奖次数
        Integer maxenter = (Integer) redisUtil.hget(RedisKeys.MAXENTER + gameid, user.getLevel() + "");
        //如果没设置，默认为0，即：不限制次数
        maxenter = maxenter == null ? 0 : maxenter;

        //用户已中奖次数
        Integer count = (Integer) redisUtil.get(RedisKeys.USERHIT + gameid + "_" + user.getId());
        if (count == null) {
            count = 0;
        }
        //根据会员等级，获取本活动允许的最大中奖数
        Integer maxcount = (Integer) redisUtil.hget(RedisKeys.MAXGOAL + gameid, user.getLevel() + "");
        //如果没设置，默认为0，即：不限制次数
        maxcount = maxcount == null ? 0 : maxcount;

        //幸运转盘类，先给用户随机剔除，再获取令牌，有就中，没有就说明抢光了
        //一般这种情况会设置足够的商品，卡在随机上
        Integer randomRate = (Integer) redisUtil.hget(RedisKeys.RANDOMRATE + gameid, user.getLevel() + "");
        if (randomRate == null) {
            randomRate = 100;
        }

        Map map = new HashMap();
        map.put("maxenter", maxenter);
        map.put("enter", enter);
        map.put("maxcount", maxcount);
        map.put("count", count);
        map.put("randomRate", randomRate);

        return new ApiResult<>(1, "成功", map);
    }

    @GetMapping("/go/{gameid}")
    @ApiOperation(value = "抽奖")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "gameid", value = "活动id", example = "1", required = true)
    })
    public ApiResult<Object> act(@PathVariable int gameid, HttpServletRequest request) {
        //TODO
        Date now = new Date();
        //活动的基本信息
        CardGame cardGame = (CardGame) redisUtil.get(RedisKeys.INFO + gameid);
        if (cardGame == null) {
            return new ApiResult<>(-1, "该活动未加载", null);
        }
        //判断活动是否开始
        if (cardGame.getStarttime().after(now)) {
            return new ApiResult<>(-1, "活动未开始", null);
        }
        if (now.after(cardGame.getEndtime())) {
            return new ApiResult<>(-1, "活动已结束", null);
        }
        HttpSession session = request.getSession();
        CardUser cardUser = (CardUser) session.getAttribute("user");
        if (cardUser == null) {
            return new ApiResult<>(-1, "用户登录超时", null);
        } else {
            //第一次抽奖,发送消息队列，用于记录参与的活动redis分布式锁
            if (redisUtil.setNx(RedisKeys.USERGAME + cardUser.getId() + "_" + gameid,
                    1,
                    (cardGame.getEndtime().getTime() - now.getTime()) / 1000)) {
                CardUserGame cardUserGame = new CardUserGame();
                cardUserGame.setUserid(cardUser.getId());
                cardUserGame.setGameid(gameid);
                cardUserGame.setCreatetime(new Date());
                rabbitTemplate.convertAndSend(RabbitKeys.EXCHANGE_DIRECT, RabbitKeys.QUEUE_PLAY, JSON.toJSONString(cardUserGame));
            }
        }

        //根据会员等级，获取本活动允许的最大抽奖次数
        Integer maxenter = (Integer) redisUtil.hget(RedisKeys.MAXENTER + gameid, cardUser.getLevel() + "");
        //如果没设置，默认为0，不限制次数
        maxenter = maxenter == null ? 0 : maxenter;

        if (maxenter > 0) {
            //用户可抽奖次数
            long enter = redisUtil.incr(RedisKeys.USERENTER + gameid + "_" + cardUser.getId(), 1);
            if (enter > maxenter) {
                //如果达到最大次数，不允许抽奖
                return new ApiResult<>(-1, "您的抽奖次数已经用完了", null);
            }
        }

        //根据会员等级，获取本活动允许的最大中奖
        Integer maxgoal = (Integer) redisUtil.hget(RedisKeys.MAXGOAL + cardUser.getId(), cardUser.getLevel() + "");
        //如果不设置，默认为0，即：不限制次数
        maxgoal = maxgoal == null ? 0 : maxgoal;

        //获取合法的token
        Long token;
        switch (cardGame.getType()) {
            //时间随机
            case 1:
                //随机类比较麻烦，需要按设计时序图走
                //使用lua调用redis，保持原子性
                token = luaScript.tokenCheck(gameid, cardUser.getId(), maxgoal);
                if (token == 0) {
                    return new ApiResult<>(0, "未中奖", null);
                } else if (token == -1) {
                    return new ApiResult<>(-1, "您已到达最大抽奖次数", null);
                } else if (token == -2) {
                    return new ApiResult<>(-1, "奖品已经抽完了", null);
                }
                break;
            case 2:
                //瞬间秒杀类，直接获取令牌，有就中，没有就说明抢光了
                token = (Long) redisUtil.leftPop(RedisKeys.TOKENS + gameid);
                if (token == null) {
                    return new ApiResult<>(-1, "奖品已抽光", null);
                }
                break;
            case 3:
                //幸运转盘类型，先给用户随机剔除，再获取令牌，有就中，没有就说明抢光了
                //这种情况一般会设置足够的商品，卡在随机上
                Integer randomRate = (Integer) redisUtil.hget(RedisKeys.RANDOMRATE + cardUser.getId(), cardUser.getLevel() + "");
                if (randomRate == null) {
                    randomRate = 100;
                }
                //每次请求取一个0-100的随机数，如果这个数没有落在范围内，直接返回未中奖
                if (new Random().nextInt(100) > randomRate) {
                    return new ApiResult<>(-1, "未中奖", null);
                }

                token = (Long) redisUtil.leftPop(RedisKeys.TOKENS + gameid);
                if (token == null) {
                    return new ApiResult<>(-1, "奖品已经被抽光了", null);
                }
                break;
            default:
                return new ApiResult<>(-1, "不支持的活动类型", null);
        }

        //拿到合法的token
        //抽中的奖品
        CardProduct product = (CardProduct) redisUtil.get(RedisKeys.TOKEN + gameid + "_" + token);
        //投放消息给队列，中奖后的耗时业务，交给消息模块处理
        CardUserHit cardUserHit = new CardUserHit();
        cardUserHit.setGameid(gameid);
        cardUserHit.setHittime(now);
        cardUserHit.setProductid(product.getId());
        cardUserHit.setUserid(cardUser.getId());
        rabbitTemplate.convertAndSend(RabbitKeys.EXCHANGE_DIRECT, RabbitKeys.QUEUE_HIT, JSON.toJSONString(cardUserHit));
        return new ApiResult<>(1, "恭喜中奖了", product);
    }

    @GetMapping("/info/{gameid}")
    @ApiOperation(value = "缓存信息")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "gameid", value = "活动id", example = "1", required = true)
    })
    public ApiResult info(@PathVariable int gameid) {
        //TODO
        Map map = new LinkedHashMap<>();
        map.put(RedisKeys.INFO + gameid, redisUtil.get(RedisKeys.INFO + gameid));

        List<Object> tokens = redisUtil.lrange(RedisKeys.TOKENS + gameid, 0, -1);
        Map tokenMap = new LinkedHashMap<>();
        for (Object token : tokens) {
            tokenMap.put(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss.SSS").format(new Date(Long.valueOf(token.toString()) / 1000)),
                    redisUtil.get("RedisKeys." + gameid + "_" + token));
        }
        map.put(RedisKeys.TOKENS + gameid, tokenMap);
        map.put(RedisKeys.MAXGOAL + gameid, redisUtil.hmget(RedisKeys.MAXGOAL + gameid));
        map.put(RedisKeys.MAXENTER + gameid, redisUtil.hmget(RedisKeys.MAXENTER + gameid));
        return new ApiResult<>(1, "缓存信息", map);
    }
}
